﻿--MacaroonCB, a World of Warcraft® user interface addon.
--Copyright© 2008 Connor H. Chenoweth, aka Maul

Macaroon.CBBars = {}
Macaroon.CBCastbars = {}

MacaroonCBSavedState = {}

local find = string.find
local format = string.format
local gsub = string.gsub
local lower = string.lower
local sort = table.sort

local currentVersion = 30000.11
local xpFrameData = {}
local playerEnteredWorld, load, clickedBar, ss = false

local unitNames = { "-none-", "player", "pet", "target", "targettarget", "focus", "mouseover", "party1", "party2", "party3", "party4" }

local barTextures = {
	[1] = { "Default", "Interface\\TargetingFrame\\BarFill2" },
	[2] = { "Contrast", "Interface\\AddOns\\Macaroon\\Images\\BarFill_contrast" },
	-- Following textures by Tonedef of WoWInterface
	[3] = { "Carpaint", "Interface\\AddOns\\Macaroon\\Images\\BarFill_Carpaint" },
	[4] = { "Gel", "Interface\\AddOns\\Macaroon\\Images\\BarFill_Gel" },
	[5] = { "Glassed", "Interface\\AddOns\\Macaroon\\Images\\BarFill_Glassed" },
	[6] = { "Soft", "Interface\\AddOns\\Macaroon\\Images\\BarFill_Soft" },
	[7] = { "Velvet", "Interface\\AddOns\\Macaroon\\Images\\BarFill_Velvet" },
}

local barBorders = {
	[1] = { "Tooltip", "Interface\\Tooltips\\UI-Tooltip-Border", 2, 2, 3, 3, 12, 12, -2, 3, 2, -3 },
	[2] = { "Slider", "Interface\\Buttons\\UI-SliderBar-Border", 3, 3, 6, 6, 8, 8 , -1, 5, 1, -5 },
	[3] = { "Dialog", "Interface\\AddOns\\Macaroon\\Images\\Border_Dialog", 11, 12, 12, 11, 26, 26, -7, 7, 7, -7 },
	[4] = { "None", "", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
}

local toggleOptions = {
	showIcon = MACAROONCB_STRINGS.CASTBAR_CHECK_1,
	showSpell = MACAROONCB_STRINGS.CASTBAR_CHECK_2,
	showTimer = MACAROONCB_STRINGS.CASTBAR_CHECK_3,
}


local UnitCastingInfo = _G.UnitCastingInfo
local GetTime = _G.GetTime

local copyTable = Macaroon.copyTable
local clearTable = Macaroon.clearTable
local getChildrenAndRegions = Macaroon.getChildrenAndRegions
local MACAROONCB_STRINGS = MACAROONCB_STRINGS

local function castingBarSetColor(self)

	if (self.barcolor) then

		self:SetStatusBarColor(self.barcolor[1], self.barcolor[2], self.barcolor[3])
		self.background:SetBackdropBorderColor(self.bordercolor[1], self.bordercolor[2], self.bordercolor[3], 1)
		self.textleft:SetTextColor(self.textcolor[1], self.textcolor[2], self.textcolor[3])
		self.textright:SetTextColor(self.timercolor[1], self.timercolor[2], self.timercolor[3])
	else
		self:SetStatusBarColor(1.0, 0.7, 0.0)
		self.background:SetBackdropBorderColor(0.8, 0.8, 0.8, 1)
		self.textleft:SetTextColor(1, 1, 1)
		self.textright:SetTextColor(1, 1, 1)
	end
end

local function cbFrame_FinishSpell(self)

	if ( self.spark ) then
		self.spark:Hide()
	end

	if ( self.barflash ) then
		self.barflash:SetAlpha(0.0)
		self.barflash:Show()
	end

	self.flash = 1
	self.fadeOut = 1
	self.casting = nil
	self.channeling = nil
end

local function cbFrame_OnEvent(self, event, ...)

	local unit = ...

	if ( event == "PLAYER_ENTERING_WORLD" ) then
		if (self.config.unit and UnitChannelInfo(self.config.unit)) then
			event = "UNIT_SPELLCAST_CHANNEL_START"
			unit = self.config.unit
		elseif (self.config.unit and UnitCastingInfo(self.config.unit)) then
			event = "UNIT_SPELLCAST_START"
			unit = self.config.unit
		end
	end

	if (unit ~= self.config.unit) then
		return
	end

	--if (self.orient == "HORIZONTAL") then
	--	self.spark:SetWidth(20)
	--	self.spark:SetHeight(self:GetHeight()*2)
	--else
	--	self.spark:SetWidth(self:GetWidth()*2)
	--	self.spark:SetHeight(20)
	--end

	if ( event == "UNIT_SPELLCAST_START" ) then

		local name, nameSubtext, text, texture, startTime, endTime, isTradeSkill = UnitCastingInfo(unit)

		if ( not name or (not self.showTradeSkills and isTradeSkill)) then
			self:Hide()
			return
		end

		castingBarSetColor(self)

		if ( self.spark ) then
			self.spark:Show()
		end

		self.startTime = startTime / 1000
		self.maxValue = endTime / 1000
		self:SetMinMaxValues(self.startTime, self.maxValue)
		self:SetValue(self.startTime)
		self.totalTime = self.maxValue - self:GetValue()

		if ( self.textleft and self.text) then
			if (self.config.showSpell) then
				self.textleft:SetText(text)
				self.textleft:Show()
			else
				self.textleft:Hide()
			end
		end

		if ( self.textright and self.text) then
			if (self.config.showTimer) then
				self.textright:Show()
			else
				self.textright:Hide()
			end
		end

		if ( self.baricon ) then
			if ( self.config.showIcon ) then
				self.baricon:SetTexture(texture)
				self.baricon:Show()
			else
				self.baricon:Hide()
			end
		end

		self:SetAlpha(1.0)
		self.holdTime = 0
		self.casting = 1
		self.channeling = nil
		self.fadeOut = nil

		if ( self.showCastbar ) then
			self:Show()
		end
	elseif ( event == "UNIT_SPELLCAST_SUCCEEDED") then

		if (self.successcolor) then
			self:SetStatusBarColor(self.successcolor[1], self.successcolor[2], self.successcolor[3])
		end

	elseif ( event == "UNIT_SPELLCAST_STOP" or event == "UNIT_SPELLCAST_CHANNEL_STOP" ) then

		if ( not self:IsVisible() ) then
			self:Hide()
		end

		if ( self.casting or self.channeling ) then

			if ( self.spark ) then
				self.spark:Hide()
			end

			if ( self.barflash ) then
				self.barflash:SetAlpha(0.0)
				self.barflash:Show()
			end

			self:SetValue(self.maxValue)

			if ( event == "UNIT_SPELLCAST_STOP" ) then
				self.casting = nil
			else
				self.channeling = nil
			end

			self.flash = 1
			self.fadeOut = 1
			self.holdTime = 0
		end

	elseif ( event == "UNIT_SPELLCAST_FAILED" or event == "UNIT_SPELLCAST_INTERRUPTED" ) then

		--if ( self:IsShown() and not self.channeling and not self.fadeOut ) then
		if (self.maxValue and self:IsShown() and not self.channeling ) then

			self:SetValue(self.maxValue)

			if (self.failedcolor) then
				self:SetStatusBarColor(self.failedcolor[1], self.failedcolor[2], self.failedcolor[3])
			end

			if ( self.spark ) then
				self.spark:Hide()
			end

			if ( self.textleft and self.text) then
				if (self.config.showSpell) then
					if ( event == "UNIT_SPELLCAST_FAILED" ) then
						self.textleft:SetText(FAILED)
					else
						self.textleft:SetText(INTERRUPTED)
					end
				end
			end

			self.casting = nil
			self.channeling = nil
			self.fadeOut = 1
			self.holdTime = GetTime() + CASTING_BAR_HOLD_TIME
		end

	elseif ( event == "UNIT_SPELLCAST_DELAYED" ) then

		if ( self:IsShown() ) then

			local name, nameSubtext, text, texture, startTime, endTime, isTradeSkill = UnitCastingInfo(unit)

			if ( not name or (not self.showTradeSkills and isTradeSkill)) then
				self:Hide()
				return
			end

			self.startTime = startTime / 1000
			self.maxValue = endTime / 1000
			self:SetMinMaxValues(self.startTime, self.maxValue)

			if ( not self.casting ) then

				castingBarSetColor(self)

				if ( self.spark ) then
					self.spark:Show()
				end

				if ( self.barflash ) then
					self.barflash:SetAlpha(0.0)
					self.barflash:Hide()
				end

				self.casting = 1
				self.channeling = nil
				self.flash = 0
				self.fadeOut = 0
			end
		end

	elseif ( event == "UNIT_SPELLCAST_CHANNEL_START" ) then

		local name, nameSubtext, text, texture, startTime, endTime, isTradeSkill = UnitChannelInfo(unit)

		if ( not name or (not self.showTradeSkills and isTradeSkill)) then
			self:Hide()
			return
		end

		castingBarSetColor(self)

		self.startTime = startTime / 1000
		self.endTime = endTime / 1000
		self.duration = self.endTime - self.startTime
		self.maxValue = self.startTime
		self.totalTime = self.duration + self:GetValue() - self.endTime

		self:SetMinMaxValues(self.startTime, self.endTime)
		self:SetValue(self.endTime)

		if ( self.textleft and self.text) then
			if (self.config.showSpell) then
				self.textleft:SetText(text)
				self.textleft:Show()
			else
				self.textleft:Hide()
			end
		end

		if ( self.baricon ) then
			if ( self.config.showIcon) then
				self.baricon:SetTexture(texture)
				self.baricon:Show()
			else
				self.baricon:Hide()
			end
		end

		if ( self.spark ) then
			self.spark:Hide()
		end

		self:SetAlpha(1.0)
		self.holdTime = 0
		self.casting = nil
		self.channeling = 1
		self.fadeOut = nil

		if ( self.showCastbar ) then
			self:Show()
		end

	elseif ( event == "UNIT_SPELLCAST_CHANNEL_UPDATE" ) then

		if ( self:IsShown() ) then

			local name, nameSubtext, text, texture, startTime, endTime, isTradeSkill = UnitChannelInfo(unit)

			if ( not name or (not self.showTradeSkills and isTradeSkill)) then
				self:Hide()
				return
			end

			self.startTime = startTime / 1000
			self.endTime = endTime / 1000
			self.maxValue = self.startTime
			self:SetMinMaxValues(self.startTime, self.endTime)
		end
	end
end

local function cbFrame_OnUpdate(self, elapsed)

	local unit, status, sparkPosition, time, barValue, alpha, timeLeft = self.config.unit

	if ( self.casting ) then
		timeLeft = self.maxValue - self:GetValue()

	elseif ( self.channeling ) then
		if (self.duration) then
			timeLeft = self.duration + self:GetValue() - self.endTime
		end

	end

	-- Casting bar timer code thanks to Luinil of Suramar server
	if ( timeLeft ) then

		timeleft = (timeLeft < 0.1) and 0.01 or timeLeft

		displayName = self.timer.castingInfo[unit][1]

		if (self.text) then
			if (self.config.showSpell) then
				self.textleft:SetText(self.timer.castingInfo[unit][1])
			end
			if (self.config.showTimer) then
				if (self.totalTime) then
					self.textright:SetText(format(self.timer.castingInfo[unit][2], timeLeft).."/"..format(self.timer.castingInfo[unit][2], self.totalTime))
				else
					self.textright:SetText(format(self.timer.castingInfo[unit][2], timeLeft))
				end
			end
		end

		if (self:GetOrientation() == "VERTICAL") then
			self.textleft:SetText("")
			self.textright:SetText("")
		end
	end

	if ( self.casting ) then

		status = GetTime()

		if ( status > self.maxValue ) then
			status = self.maxValue
		end

		if ( status == self.maxValue ) then
			self:SetValue(self.maxValue)
			cbFrame_FinishSpell(self)
			return
		end

		self:SetValue(status)

		if ( self.barflash ) then
			self.barflash:Hide()
		end

		if (self.orient == "VERTICAL") then

			sparkPosition = ((status - self.startTime) / (self.maxValue - self.startTime)) * self:GetHeight()

			if ( sparkPosition < 0 ) then
				sparkPosition = 0
			end

			if ( self.spark ) then
				self.spark:SetPoint("CENTER", self, "BOTTOM", 0, sparkPosition)
			end

		else
			sparkPosition = ((status - self.startTime) / (self.maxValue - self.startTime)) * self:GetWidth()

			if ( sparkPosition < 0 ) then
				sparkPosition = 0
			end

			if ( self.spark ) then
				self.spark:SetPoint("CENTER", self, "LEFT", sparkPosition, 0)
			end
		end

	elseif ( self.channeling ) then

		time = GetTime()

		if ( time > self.endTime ) then
			time = self.endTime
		end

		if ( time == self.endTime ) then
			cbFrame_FinishSpell(self)
			return
		end

		barValue = self.startTime + (self.endTime - time)

		self:SetValue( barValue )

		if ( self.barflash ) then
			self.barflash:Hide()
		end

	elseif ( GetTime() < self.holdTime ) then

		return

	elseif ( self.flash ) then

		alpha = 0

		if ( self.barflash ) then
			alpha = self.barflash:GetAlpha() + CASTING_BAR_FLASH_STEP
		end

		if ( alpha < 1 ) then
			if ( self.barflash ) then
				self.barflash:SetAlpha(alpha)
			end
		else
			if ( self.barflash ) then
				self.barflash:SetAlpha(1.0)
			end
			self.flash = nil
		end

	elseif ( self.fadeOut ) then

		alpha = self:GetAlpha() - CASTING_BAR_ALPHA_STEP

		if ( alpha > 0 ) then
			self:SetAlpha(alpha)
		else
			self.fadeOut = nil
			self:Hide()
		end
	end
end

local function setBorder(castbar)

	local backdrop = _G[castbar:GetName().."Background"]

	backdrop:SetBackdrop( { bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
				edgeFile = barBorders[castbar.config.border][2],
				tile = true,
				tileSize = barBorders[castbar.config.border][7],
				edgeSize = barBorders[castbar.config.border][8],
				insets = { left = barBorders[castbar.config.border][3],
					   right = barBorders[castbar.config.border][4],
					   top = barBorders[castbar.config.border][5],
					   bottom = barBorders[castbar.config.border][6]
					 }
				} )

	backdrop:SetPoint("TOPLEFT", barBorders[castbar.config.border][9], barBorders[castbar.config.border][10])
	backdrop:SetPoint("BOTTOMRIGHT", barBorders[castbar.config.border][11], barBorders[castbar.config.border][12])

	backdrop:SetBackdropColor(0, 0, 0, 0)
	backdrop:SetBackdropBorderColor(0.8, 0.8, 0.8, 1)

	backdrop:SetFrameLevel(castbar:GetFrameLevel()+1)

	local backdrop = _G[castbar:GetName().."BarFlash"]

	backdrop:SetBackdrop( { bgFile = "",
				edgeFile = barBorders[castbar.config.border][2],
				tile = true,
				tileSize = barBorders[castbar.config.border][7],
				edgeSize = barBorders[castbar.config.border][8],
				insets = { left = barBorders[castbar.config.border][3],
					   right = barBorders[castbar.config.border][4],
					   top = barBorders[castbar.config.border][5],
					   bottom = barBorders[castbar.config.border][6]
					 }
				} )

	backdrop:SetPoint("TOPLEFT", barBorders[castbar.config.border][9], barBorders[castbar.config.border][10])
	backdrop:SetPoint("BOTTOMRIGHT", barBorders[castbar.config.border][11], barBorders[castbar.config.border][12])

	backdrop:SetBackdropColor(1, 1, 1, 0.5)
	backdrop:SetBackdropBorderColor(castbar.bordercolor[1], castbar.bordercolor[2], castbar.bordercolor[3])

	backdrop = _G[castbar:GetName().."DummyBackground"]

	backdrop:SetBackdrop( { bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
				edgeFile = barBorders[castbar.config.border][2],
				tile = true,
				tileSize = barBorders[castbar.config.border][7],
				edgeSize = barBorders[castbar.config.border][8],
				insets = { left = barBorders[castbar.config.border][3],
					   right = barBorders[castbar.config.border][4],
					   top = barBorders[castbar.config.border][5],
					   bottom = barBorders[castbar.config.border][6]
					 }
				} )

	backdrop:SetPoint("TOPLEFT", barBorders[castbar.config.border][9], barBorders[castbar.config.border][10])
	backdrop:SetPoint("BOTTOMRIGHT", barBorders[castbar.config.border][11], barBorders[castbar.config.border][12])

	backdrop:SetBackdropColor(0, 0, 0, 0)
	backdrop:SetBackdropBorderColor(castbar.bordercolor[1], castbar.bordercolor[2], castbar.bordercolor[3])

	backdrop:SetFrameLevel(castbar.dummy:GetFrameLevel()+1)
end

local function updateCastBarData(castbar, bar, state)

	castbar.bar = bar
	castbar.alpha = bar.config.alpha
	castbar.showGrid = bar.config.showGrid
	castbar:SetAttribute("showgrid-bar", bar.config.showGrid)
	castbar.config.showstates = state
	castbar:SetAttribute("showstates", state)

	castbar:SetWidth(castbar.config.width)
	castbar:SetHeight(castbar.config.height)

	--barBorders
	castbar.barcolor = bar.config.visuals.color1
	castbar.bordercolor = bar.config.visuals.color2
	castbar.successcolor = bar.config.visuals.color3
	castbar.failedcolor = bar.config.visuals.color4
	castbar.textcolor = bar.config.visuals.color5
	castbar.timercolor = bar.config.visuals.color6

	castbar:SetOrientation(bar.config.visuals.orientation)
	castbar:SetStatusBarTexture(barTextures[castbar.config.texture][2])
	castbar.dummy:SetStatusBarTexture(barTextures[castbar.config.texture][2])

	setBorder(castbar)

	castbar.config.bar = bar:GetID()
	castbar.config.stored = false

	castbar:SetFrameStrata(bar.config.buttonStrata)
	castbar:SetFrameLevel(4)

	castbar.dummy:SetFrameStrata(bar.config.barStrata)
	castbar.dummy:SetFrameLevel(5)

	if (bar.handler) then

		if (not castbar:IsVisible()) then

			castbar.dummy:SetParent(bar)
			castbar.dummy:Show()

			Macaroon.UpdateButtonVisibility(castbar, bar.handler:GetAttribute("state-current"))

			if (not castbar:GetAttribute("editmode")) then
				castbar:Hide()
			end
		end
	end

	castbar.editframetype:SetText("")

	Macaroon.EditFrames[castbar] = castbar.editframe
end

local function updateWidth(editframe, castbar, delta)

	local modifier = math.ceil(editframe.elapsed/0.5)

	if (modifier <= 0) then
		modifier = 1
	end

	if (delta > 0 ) then
		castbar.config.width = castbar.config.width + 0.5 * modifier
	else
		castbar.config.width = castbar.config.width - 0.5 * modifier
		if (castbar.config.width < 10) then
			castbar.config.width = 10
		end
	end

	castbar.bar.updateBar(castbar.bar, nil, true, true)

end

local function updateHeight(editframe, castbar, delta)

	local modifier = math.ceil(editframe.elapsed/0.5)

	if (modifier <= 0) then
		modifier = 1
	end

	if (delta > 0 ) then
		castbar.config.height = castbar.config.height + 0.5 * modifier
	else
		castbar.config.height = castbar.config.height - 0.5 * modifier
		if (castbar.config.height < 4) then
			castbar.config.height = 4
		end
	end

	castbar.bar.updateBar(castbar.bar, nil, true, true)
end

local function updateTexture(editframe, castbar, delta)

	local index = castbar.config.texture

	if (delta > 0) then

		if (index == 1) then
			index = #barTextures
		else
			index = index - 1
		end

		castbar.config.texture = index

	else

		if (index == #barTextures) then
			index = 1
		else
			index = index + 1
		end

		castbar.config.texture = index
	end

	castbar:SetStatusBarTexture(barTextures[index][2])
	castbar.dummy:SetStatusBarTexture(barTextures[index][2])
end

local function updateBorder(editframe, castbar, delta)

	local index = castbar.config.border

	if (delta > 0) then

		if (index == 1) then
			index = #barBorders
		else
			index = index - 1
		end

		castbar.config.border = index

	else

		if (index == #barBorders) then
			index = 1
		else
			index = index + 1
		end

		castbar.config.border = index
	end

	setBorder(castbar)
end

local actionTable = {
	[MACAROONCB_STRINGS.CASTBAR_EDIT_BUTTON_1] = updateWidth,
	[MACAROONCB_STRINGS.CASTBAR_EDIT_BUTTON_2] = updateHeight,
	[MACAROONCB_STRINGS.CASTBAR_EDIT_BUTTON_3] = updateTexture,
	[MACAROONCB_STRINGS.CASTBAR_EDIT_BUTTON_4] = updateBorder,
}

local function updateValues(self, delta)

	if (actionTable[self.action]) then
		actionTable[self.action](self, self:GetParent(), delta)
	end
end

local function castbarDefaults(index, castbar)

	castbar.config = {
		type = "castbar",
		bar = 0,
		barPos = 0,
		showstates = "",
		laststate = "",
		scale = 1,
		XOffset = 0,
		YOffset = 0,
		width = 200,
		height = 12,
		unit = "player",
		texture = 1,
		border = 1,
		showIcon = false,
		showSpell = true,
		showTimer = true,
	}
end

local function createCastBar(index)

	local castbar

	if (_G["MacaroonCastBar"..index]) then
		castbar = _G["MacaroonCastBar"..index]
	else
		castbar = CreateFrame("StatusBar", "MacaroonCastBar"..index, UIParent, "MacaroonCBTemplate")
	end

	local castbarName = castbar:GetName()
	local objects = getChildrenAndRegions(castbar)

	for k,v in pairs(objects) do
		local name = gsub(v, castbar:GetName(), "")
		castbar[lower(name)] = _G[v]
	end

	castbar:SetID(index)
	castbar.id = index

	castbarDefaults(index, castbar)

	objects = getChildrenAndRegions(castbar)

	for k,v in pairs(objects) do
		local name = gsub(v, castbar:GetName(), "")
		castbar[lower(name)] = _G[v]
	end

	castbar.updateButtonData = updateCastBarData
	castbar.editframe.updateValues = updateValues

	castbar:SetAttribute("showgrid", 0)
	castbar:SetAttribute("showstates", "")
	castbar:SetAttribute("hotkeys", "")
	castbar:SetAttribute("hasaction", true)
	castbar:SetAttribute("_childupdate", [[

			self:SetAttribute("stateshown", false)

			for showstate in gmatch(self:GetAttribute("showstates"), "[^;]+") do
				if (message and strfind(message, showstate)) then
					if (self:GetAttribute("hasaction") or self:GetAttribute("showgrid-bar") or self:GetAttribute("editmode")) then
						self:Show()
					end
					self:SetAttribute("stateshown", true)
				end
			end

			if (not self:GetAttribute("stateshown")) then
				self:Hide()
			end
		]] )

	if (not ss.castbars) then ss.castbars = {} end

	ss.castbars[index] = { castbar.config }

	Macaroon.CBCastbars[index] = { castbar, 1 }

	return castbar
end

local function castbar_Show(castbar)

	if (not InCombatLockdown()) then

		if (castbar:GetAttribute("stateshown")) then
			castbar:Show()
			castbar:SetAlpha(1)

			if (castbar:GetAttribute("editmode")) then
				castbar.dummy:SetParent(castbar)
				castbar.dummy:Show()
			end
		end
	end
end

local function castbar_Hide(castbar)

	if (not InCombatLockdown()) then

		if (not castbar.showGrid and not castbar.casting and not castbar.channeling) then
			castbar:Hide()
			castbar.dummy:SetParent(castbar.bar)
		end
	end
end

local function castbars_Show(edit)

	for k,v in pairs(Macaroon.CBCastbars) do

		if (edit) then
			v[1]:SetAttribute("editmode", true)
		end

		castbar_Show(v[1])
	end
end

tinsert(Macaroon.ShowGrids, castbars_Show)

local function castbars_Hide(edit)

	for k,v in pairs(Macaroon.CBCastbars) do

		if (edit) then
			v[1]:SetAttribute("editmode", false)
		end

		castbar_Hide(v[1])
	end
end

tinsert(Macaroon.HideGrids, castbars_Hide)

function Macaroon.CBFrame_OnLoad(self, unit, showTradeSkills, text)

	self:RegisterEvent("UNIT_SPELLCAST_START")
	self:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
	self:RegisterEvent("UNIT_SPELLCAST_STOP")
	self:RegisterEvent("UNIT_SPELLCAST_FAILED")
	self:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
	self:RegisterEvent("UNIT_SPELLCAST_DELAYED")
	self:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
	self:RegisterEvent("UNIT_SPELLCAST_CHANNEL_UPDATE")
	self:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
	self:RegisterEvent("PLAYER_ENTERING_WORLD")

	self.castingInfo = {}
	self.text = text
	self.showTradeSkills = showTradeSkills
	self.casting = nil
	self.channeling = nil
	self.holdTime = 0
	self.showCastbar = true

	self:SetScript("OnEvent", cbFrame_OnEvent)
	self:SetScript("OnUpdate", cbFrame_OnUpdate)

	self:Hide()
end

function Macaroon.CBFrameTimer_OnLoad(self)

	self.castingInfo = {}
	self:RegisterEvent("UNIT_SPELLCAST_START")
	self:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
	self:RegisterEvent("UNIT_SPELLCAST_STOP")
	self:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
end

function Macaroon.CBFrameTimer_OnEvent(self, event, ...)

	local unit = select(1, ...)

	if (unit) then

		if ( event == "UNIT_SPELLCAST_START" ) then

			local _, _, text = UnitCastingInfo(unit)

			if (not self.castingInfo[unit]) then self.castingInfo[unit] = {} end
			self.castingInfo[unit][1] = text
			self.castingInfo[unit][2] = "%0.1f"

		elseif ( event == "UNIT_SPELLCAST_CHANNEL_START" ) then

			local _, _, text = UnitChannelInfo(unit)

			if (not self.castingInfo[unit]) then self.castingInfo[unit] = {} end
			self.castingInfo[unit][1] = text
			self.castingInfo[unit][2] = "%0.1f"
		end
	end
end

local uniqueBarDefaults = {

	columns = 1,
	padH = 5,
	padV = 6,
	visuals = {
		dimensions = { 200, 12 },
		orientation = "HORIZONTAL",
		color1 = { 1, 0.7, 0 },
		color2 = { 0.8, 0.8, 0.8 },
		color3 = { 0, 1, 0 },
		color4 = { 1, 0, 0 },
		color5 = { 1, 1, 1 },
		color6 = { 1, 1, 1 },
	},
}

function Macaroon.CBBarDefaults(bar)

	for k,v in pairs(uniqueBarDefaults) do

		if (type(v) == "table") then
			bar.config[k] = copyTable(v)
		else
			bar.config[k] = v
		end
	end

	bar.config.name = "Cast Bar "..bar:GetID()

	bar.handler:SetAttribute("state-current", bar.config.currentstate)
	bar.handler:SetAttribute("state-last", bar.config.laststate)
end

local function updateVariables(element, defaultConfig)

	-- Add new vars
	for key,value in pairs(defaultConfig) do

		if (element.config[key] == nil) then

			if (element.config[lower(key)] ~= nil) then

				element.config[key] = element.config[lower(key)]
				element.config[lower(key)] = nil
			else
				element.config[key] = value
			end
		end
	end
	-- Add new vars

	-- Kill old vars
	for key,value in pairs(element.config) do
		if (defaultConfig[key] == nil) then
			element.config[key] = nil
		end
	end
	-- Kill old vars
end

function Macaroon.CBLoadSavedData(saved, profile)

	if (saved) then

		local savedState = copyTable(saved)
		local newBar, defaultConfig = false

		if (savedState.cbbars) then

			clearTable(Macaroon.CBBars)

			defaultConfig = Macaroon.GetBarDefaults()

			for k,v in pairs(savedState.cbbars) do

				local bar = Macaroon.CreateBar("cast", true, true)

				bar.config = copyTable(savedState.cbbars[k][1])

				updateVariables(bar, defaultConfig)

				bar.handler:SetAttribute("state-current", bar.config.currentstate)

				bar.handler:SetAttribute("state-last", bar.config.laststate)

			end

			savedState.cbbars = nil

		elseif (not profile) then

			newBar = Macaroon.CreateBar("cast", true, true)
		end

		if (savedState.castbars) then

			for k,v in pairs(Macaroon.CBCastbars) do
				Macaroon.StoreButton(v[1], Macaroon.CBCastbars)
			end

			defaultConfig = Macaroon.GetCBCastbarDefaults()

			for k,v in pairs(savedState.castbars) do

				local castbar = createCastBar(k)

				castbar.config = copyTable(savedState.castbars[k][1])

				updateVariables(castbar, defaultConfig)
			end

			savedState.castbars = nil

		elseif (not profile) then

			createCastBar(1)
		end

		if (newBar) then
			Macaroon.AddButton(1, newBar)
		end

		if (newBar) then
			Macaroon.Ypos = Macaroon.UpdateBarPositions(Macaroon.CBBars, true, Macaroon.Ypos)
		end

		for k,v in pairs(savedState) do
			if (ss[k]) then
				ss[k] = v
			end
		end

		load = true
	end
end

function Macaroon.CBUpdateElements()

	for k,v in pairs(Macaroon.CBBars) do

		v.stateschanged = true

		v.castbarCountChanged = true

		v.updateBar(v, true, true, true, true)
	end

	for k,v in pairs(Macaroon.CBBars) do

		v.updateBarLink(v)
	end
end

function Macaroon.CBSaveCurrentState()

	if (load) then

		if (ss.cbbars) then
			clearTable(ss.cbbars)
		else
			ss.cbbars = {}
		end

		for k,v in pairs(Macaroon.CBBars) do
			ss.cbbars[k] = { v.config }
		end

		if (ss.castbars) then
			clearTable(ss.castbars)
		else
			ss.castbars = {}
		end

		for k,v in pairs(Macaroon.CBCastbars) do
			ss.castbars[k] = { v[1].config }
		end

		ss.savedVersion = currentVersion
	end

	return ss, "cbbars", "castbars"
end

function Macaroon.CBBarDefaults(bar)

	bar.config.name = "Cast Bar "..bar:GetID()
	bar.config.columns = 1
	bar.config.padH = 5
	bar.config.padV = 6
	bar.config.visuals.dimensions[1] = 200
	bar.config.visuals.dimensions[2] = 12
	bar.config.visuals.orientation = "HORIZONTAL"
	bar.config.visuals.color1 = { 1, 0.7, 0 }
	bar.config.visuals.color2 = { 0.8, 0.8, 0.8 }
	bar.config.visuals.color3 = { 0, 1, 0 }
	bar.config.visuals.color4 = { 1, 0, 0 }
	bar.config.visuals.color5 = { 1, 1, 1 }
	bar.config.visuals.color6 = { 1, 1, 1 }

	bar.handler:SetAttribute("state-current", bar.config.currentstate)
	bar.handler:SetAttribute("state-last", bar.config.laststate)
end

function Macaroon.GetCBCastbarDefaults()

	local defaults = {}

	castbarDefaults(0, defaults)

	return defaults.config
end

function Macaroon.AddCastBar(bar)

	local index, made, castbar = 1, false

	while not made do

		if (not _G[bar.btnType..index]) then
			castbar = createCastBar(index)
			made = true
		end

		index = index + 1
	end

	return castbar
end

function Macaroon.CastbarEditor_OnClick(self, action)

	PlaySound("gsTitleOptionOK")

	local editframe = Macaroon.CurrentButton.editframe

	if (action) then

		editframe.message:SetText(MACAROONCB_STRINGS["CASTBAR_EDIT_BUTTON_"..self:GetID()])

		editframe.hover = true

		editframe.action = action
	end

	MacaroonButtonEditor.shrink = true
end

function Macaroon.CastbarEditorCheck_OnClick(self, action)

	PlaySound("gsTitleOptionOK")

	local castbar, checked, option = Macaroon.CurrentButton, self:GetChecked()

	for k,v in pairs(toggleOptions) do
		if (v == action) then
			option = k
		end
	end

	if (castbar) then

		if (checked) then
			castbar.config[option] = true
		else
			castbar.config[option] = false
		end
	end
end

local function target_OnShow(self)

	local castbar = Macaroon.CurrentButton

	if (castbar) then

		local data = {}

		for k,v in pairs(unitNames) do
			data[v] = k
		end

		Macaroon.EditBox_PopUpInitialize(self.popup, data)
	end

end

local function target_OnTextChanged(self)

	local castbar = Macaroon.CurrentButton

	if (castbar) then

		if (self:GetText() == "-none-") then
			castbar.config.unit = false
		else
			castbar.config.unit = self:GetText()
		end
	end
end

function Macaroon.CastbarEditor_OnLoad(self)

	local breakIndex, xOffset, index, frame, lastFrame, anchorFrame, lastButton = 1, 5, 1

	index = 1

	while (MACAROONCB_STRINGS["CASTBAR_EDIT_BUTTON_"..breakIndex]) do
		breakIndex = breakIndex + 1
	end

	local yOffset = (self:GetHeight()-((breakIndex+1)*17))/breakIndex+1

	while (MACAROONCB_STRINGS["CASTBAR_EDIT_BUTTON_"..index]) do

		frame = CreateFrame("Button", "$parentButton"..index, self, "MacaroonButtonTemplate2")
		frame:SetID(index)
		frame:SetWidth(90)
		frame:SetHeight(20)
		frame:SetScript("OnClick", function(self, button) Macaroon.CastbarEditor_OnClick(self, self.action) end)
		frame.text = _G[frame:GetName().."Text"]
		frame.text:SetText(MACAROONCB_STRINGS["CASTBAR_EDIT_BUTTON_"..index])
		frame.action = frame.text:GetText()

		if (index == 1 or index == breakIndex) then
			if (anchorFrame) then
				frame:SetPoint("TOPLEFT", anchorFrame, "TOPRIGHT", 2, 0)
				anchorFrame = frame
				breakIndex = breakIndex + (breakIndex - 1)
			else
				frame:SetPoint("TOPLEFT", self, "TOPLEFT", xOffset, -5)
				anchorFrame = frame
			end
			lastFrame = frame
		else
			frame:SetPoint("TOP", lastFrame, "BOTTOM", 0, -(yOffset))
			lastFrame = frame
		end

		lastButton = frame

		index = index + 1
	end

	breakIndex = 1

	while (MACAROONCB_STRINGS["CASTBAR_CHECK_"..breakIndex]) do
		breakIndex = breakIndex + 1
	end

	yOffset = (self:GetHeight()-((breakIndex+1)*20))/breakIndex+1

	xOffset, index, frame, lastFrame, anchorFrame, iOffset = -123, 1, nil, nil, nil, 0

	while (MACAROONCB_STRINGS["CASTBAR_CHECK_"..index]) do

		frame = CreateFrame("CheckButton", "$parentCheck"..index-iOffset, self, "MacaroonOptionCBTemplate")
		frame:SetID(index-iOffset)
		frame:SetScript("OnClick", function(self, button) Macaroon.CastbarEditorCheck_OnClick(self, self.action) end)
		frame:SetScript("OnShow", function(self) end)
		frame.text = _G[frame:GetName().."Text"]
		frame.text:SetText(MACAROONCB_STRINGS["CASTBAR_CHECK_"..index])
		frame.action = frame.text:GetText()

		if (index == 1 or index == breakIndex) then
			if (anchorFrame) then
				frame:SetPoint("TOPLEFT", anchorFrame, "TOPRIGHT", 2, 0)
				anchorFrame = frame
				breakIndex = breakIndex + (breakIndex - 1)
			else
				frame:SetPoint("TOPRIGHT", self, "TOPRIGHT", xOffset, -25)
				anchorFrame = frame
			end
			lastFrame = frame
		else
			frame:SetPoint("TOP", lastFrame, "BOTTOM", 0, -(yOffset))
			lastFrame = frame
		end

		index = index + 1
	end

	frame = CreateFrame("EditBox", "$parentTarget", self, "MacaroonEditBoxTemplate1")
	frame:SetWidth(90)
	frame:SetHeight(20)
	frame:SetTextInsets(7,3,0,0)
	frame.text:SetText("Unit:")
	frame:SetPoint("TOPRIGHT", -22, -5)
	frame:SetScript("OnTextChanged", target_OnTextChanged)
	frame:SetScript("OnShow", target_OnShow)
	self.unit = frame

	Macaroon.ButtonEdit_AddPanel(self, "castbar")

	self:Hide()
end

function Macaroon.CastbarEditor_OnShow(self)

	local castbar, index, frame = Macaroon.CurrentButton, 1

	if (castbar) then

		if (castbar.config.unit) then
			self.unit:SetText(castbar.config.unit)
		else
			self.unit:SetText("-none-")
		end

		self.unit:SetCursorPosition(0)

		while (_G["MacaroonButtonEditorCastbarEditCheck"..index]) do

			frame = _G["MacaroonButtonEditorCastbarEditCheck"..index]

			for k,v in pairs(toggleOptions) do
				if (v == frame.action) then
					if (castbar.config[k]) then
						frame:SetChecked(1)
					else
						frame:SetChecked(nil)
					end
				end
			end

			index = index + 1
		end

		castbar.action = nil
	end
end

function Macaroon.CastSetSaved()

	if (MacaroonSavedState.useShared and MacaroonSharedSavedState[MacaroonSavedState.sharedProfile]) then
		ss = MacaroonSharedSavedState[MacaroonSavedState.sharedProfile]

		if (not ss) then
			ss = MacaroonCBSavedState
		end
	else
		ss = MacaroonCBSavedState
	end

	return ss
end

local function controlOnEvent(self, event, ...)

	if (event == "VARIABLES_LOADED") then

		Macaroon.CastSetSaved()

		CastingBarFrame:UnregisterAllEvents()
		CastingBarFrame:Hide()

		Macaroon.CreateBarTypes.cast = { "CBbars", ss, Macaroon.CBBars, Macaroon.CBCastbars, "MacaroonCastBar", Macaroon.AddCastBar, Macaroon.CBBarDefaults, "3Casting Bar" }

		Macaroon.StatesToSave.cast = Macaroon.CBSaveCurrentState
		Macaroon.SavedDataLoad.cast = Macaroon.CBLoadSavedData
		Macaroon.SavedDataUpdate.cast = Macaroon.CBUpdateElements
		Macaroon.SetSavedVars.cast = Macaroon.CastSetSaved

	elseif (event == "PLAYER_LOGIN") then

		Macaroon.CBLoadSavedData(ss)

	elseif (event == "PLAYER_ENTERING_WORLD" and not playerEnteredWorld) then

		Macaroon.CBUpdateElements()

		playerEnteredWorld = true
	end
end

local frame = CreateFrame("Frame", nil, UIParent)
frame:SetScript("OnEvent", controlOnEvent)
frame:RegisterEvent("VARIABLES_LOADED")
frame:RegisterEvent("PLAYER_LOGIN")
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
frame:RegisterEvent("ZONE_CHANGED_NEW_AREA")
frame:RegisterEvent("PLAYER_AURAS_CHANGED")